package com.googlecode.cswish.struts.spring;

import java.lang.annotation.Annotation;
import java.util.concurrent.Executor;

import org.aopalliance.aop.Advice;
import org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncAnnotationAdvisor;
import org.springframework.util.Assert;

/**
 * Override the default spring Async implementation, support the cross site variable
 * 
 * @author Jerry
 *
 */
public class AsyncAnnotationBeanPostProcessor extends
		AbstractAdvisingBeanPostProcessor implements BeanFactoryAware {

	private Class<? extends Annotation> asyncAnnotationType;

	private Executor executor;

	public AsyncAnnotationBeanPostProcessor() {
		setBeforeExistingAdvisors(true);
	}

	/**
	 * Set the 'async' annotation type to be detected at either class or method
	 * level. By default, both the {@link Async} annotation and the EJB 3.1
	 * {@code javax.ejb.Asynchronous} annotation will be detected.
	 * <p>
	 * This setter property exists so that developers can provide their own
	 * (non-Spring-specific) annotation type to indicate that a method (or all
	 * methods of a given class) should be invoked asynchronously.
	 * 
	 * @param asyncAnnotationType
	 *            the desired annotation type
	 */
	public void setAsyncAnnotationType(
			Class<? extends Annotation> asyncAnnotationType) {
		Assert.notNull(asyncAnnotationType,
				"'asyncAnnotationType' must not be null");
		this.asyncAnnotationType = asyncAnnotationType;
	}

	/**
	 * Set the {@link Executor} to use when invoking methods asynchronously.
	 */
	public void setExecutor(Executor executor) {
		this.executor = executor;
	}

	public void setBeanFactory(BeanFactory beanFactory) {
		if (this.executor == null) {
			this.executor = new SimpleAsyncTaskExecutor();
		}
		AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor) {
			@Override
			protected Advice buildAdvice(Executor executor) {
				return new SiteAnnotationAsyncExecutionInterceptor(executor);
			}
		};
		if (this.asyncAnnotationType != null) {
			advisor.setAsyncAnnotationType(this.asyncAnnotationType);
		}
		advisor.setBeanFactory(beanFactory);
		this.advisor = advisor;
	}
}